//Extracts the following for different numDataPoints
//0 - centroidAvg
//1 - centroidStdDev
//2 - centroidMin
//3 - centroidMax
//4 - rmsAvg
//5 - rmsStdDev
//6 - rmsMin
//7 - rmsMax
//8 - avg fft bin with highest fval (avg highest over all windows)
//
//whenever an amplitude > threshold is detected from input, computed over
//numDataPoints 128-sample ffts that overlap by 64-samples
"127.0.0.1" => string hostname;
OscSend xmit;
xmit.setHost( hostname, 6448 );

OscSend xmit2;
xmit2.setHost( hostname, 6448 );


//Custom objects
adc => FFT f =^ RMS rms => blackhole;
f =^ Centroid centroid => blackhole;
f =^ Flux flux => blackhole;
f =^ RollOff rolloff => blackhole;
UAnaBlob b;

//Set up bin stuff
128 => int FFT_SIZE;
FFT_SIZE => f.size;
Windowing.hamming(64) => f.window;

1::second / 1::samp => float SR;
SR/FFT_SIZE => float bin_width;

//constants
.03 => float threshold;
0 => int peakDetected; //flag if peak has been detected
now => time lastPeakTime;
100::samp => dur peakWindow;
10::samp => dur peakPollRate;
[1,5,10,15,20,25,30,35,40,45,50] @=> int numDataPoints[]; //list of numDataPoints values to try
1 => float rmsMultiplier; //so it isn't out of wek's range (this actually doesn't matter, leave it at 1)

0 => int currentlyAnalyzing; //flag if currently analyzing i.e. don't detect peak

//Run the peak detector in parallel to set the peakDetected flag
spork ~peakDetector();

//Extract features and send via osc when peak detected.
while (true) {
    if (peakDetected) {
        <<<"Peak detected! Analyzing">>>;
        analyzeAndSend();
    }
    .1::second => now;
}

//When peak detected, compute min, max, avg, and std. dev for centroid and rms
//and send
fun void analyzeAndSend() {  
    1 => currentlyAnalyzing;
    xmit.startMsg( "/oscCustomFeatures", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
    //Compute over all experimental values
    for (0 => int i; i < 11; i++) {
        analyzeNumDataPoints(numDataPoints[i]) @=> float result[];
        for (0 => int j; j < 9; j++) {
            result[j] => xmit.addFloat;
        }
        
    }
    <<<"done">>>;
    0 => currentlyAnalyzing;
}

//Analyze data using the specified number of data points, return the float array corresponding to
//the osc message format described at top
fun float[] analyzeNumDataPoints(int numDataPoints) {
    float centroidTotal;
    float centroidMin;
    float centroidMax;
    float centroidStdDev;
    float rmsTotal;
    float rmsMin;
    float rmsMax;
    float rmsStdDev;
    float highestBinTotal;
    new float[numDataPoints] @=> float centroidData[];
    new float[numDataPoints] @=> float rmsData[];
    
    //advance 128 samples
    128::samp=> now;
    //get 1st centroid and rms
    rms.upchuck();
    rms.fval(0) +=> rmsTotal;
    rms.fval(0) => rmsMin => rmsMax => rmsData[0];
    centroid.upchuck();
    centroid.fval(0) +=> centroidTotal;
    centroid.fval(0) => centroidMin => centroidMax => centroidData[0];
    //do rest
    for (1 => int i; i < numDataPoints; i++) {
        64::samp => now;
        rms.upchuck();
        rms.fval(0) +=> rmsTotal;
        Math.max( rms.fval(0), rmsMax ) => rmsMax;
        Math.min( rms.fval(0), rmsMin ) => rmsMin;
        rms.fval(0) => rmsData[i];
        centroid.upchuck();
        centroid.fval(0) +=> centroidTotal;
        Math.max( centroid.fval(0), centroidMax ) => centroidMax;
        Math.min( centroid.fval(0), centroidMin ) => centroidMin;
        centroid.fval(0) => centroidData[i];
        //find highest bin
        float highestBin;
        float highestBinMagnitude;
        for (1 => int j; j < FFT_SIZE; j++) {
            if (f.fval(j) > highestBinMagnitude) {
                f.fval(j) => highestBinMagnitude;
                j => highestBin;
            }
        }
        highestBin +=> highestBinTotal;
    }
    //calculate
    rmsTotal / numDataPoints => float rmsAvg;
    centroidTotal / numDataPoints => float centroidAvg;
    //get std dev
    float rmsDevSquaredTotal;
    float centroidDevSquaredTotal;
    for (0 => int i; i < numDataPoints; i++) {
        (rmsData[i] - rmsAvg)*(rmsData[i] - rmsAvg) +=> rmsDevSquaredTotal; 
        (centroidData[i] - centroidAvg)*(centroidData[i] - centroidAvg) +=> centroidDevSquaredTotal;
    }
    Math.sqrt(rmsDevSquaredTotal / numDataPoints) => rmsStdDev;
    Math.sqrt(centroidDevSquaredTotal / numDataPoints) => centroidStdDev;
    
    float result[9];
    
	centroidAvg => result[0]; 
    centroidStdDev => result[1];
    centroidMin => result[2];
    centroidMax => result[3];
    rmsAvg * rmsMultiplier => result[4];
    rmsStdDev * rmsMultiplier => result[5];
    rmsMin * rmsMultiplier => result[6];
    rmsMax * rmsMultiplier => result[7];
    highestBinTotal / numDataPoints => result[8];
    
    return result;
}

//Pretty inefficient but does the trick
//Looks for a peak in the last peakWindow 
fun void peakDetector() {
	while (true){
		if (adc.last() > threshold || adc.last() < -1 * threshold) {
			1 => peakDetected; // analyze
			now => lastPeakTime;	
		
		} else if (now > lastPeakTime + peakWindow) {
			0 => peakDetected;
		}

        peakPollRate => now; //This is silly; would be better do do a low-pass filter envelope and look for peaks there. But don't worry about it right now.

	}

}

//run in parallel to send osc feature names
//INCOMPLETE since not really worth dealing with chucks lack of string ops
fun void sendOscFeatureNames() {
    
    xmit2.startMsg( "/oscCustomFeaturesNames", "sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss");
}